package com.avblog.console.ui;

import java.util.List;
import java.util.ArrayList;
import java.awt.Toolkit;
import java.awt.Container;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.Component;
import java.awt.Color;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;

import java.awt.HeadlessException;
import java.awt.Toolkit;


import java.net.URL;
import java.io.File;
import java.io.IOException;

import javax.swing.JOptionPane;
import javax.swing.JMenuItem;
import javax.swing.UIManager;
import javax.swing.JToolBar;
import javax.swing.JInternalFrame;
import javax.swing.JTabbedPane;
import javax.swing.JButton;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.JFileChooser;
import javax.swing.JEditorPane;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.event.InternalFrameEvent;
import javax.swing.event.InternalFrameListener;
import javax.swing.event.DocumentListener;
import javax.swing.event.DocumentEvent;

import javax.swing.text.BadLocationException;
import javax.swing.text.StyledDocument;
import javax.swing.text.StyleConstants;
import javax.swing.text.Style;
import javax.swing.text.StyleContext;
import javax.swing.text.DocumentFilter;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;

import java.util.regex.Pattern;
import java.util.regex.Matcher;

import com.avblog.common.Constant;
import com.avblog.console.convert.WordRun;
import com.avblog.console.convert.WordRunKind;
import com.avblog.console.convert.Converter;


public class ConvertPane extends JInternalFrame implements IInternalFrameListener, InternalFrameListener{

	private IMainAppFrame parent;
	
	private JToolBar toolBar;
	private JButton btnLoadSource, btnConvert;
	
	private JPopupMenu popupMenu, sourcePopupMenu;
	private JMenuItem mniCopySelection, mniCopyAll, mniSourcePaste;
	
	private JTabbedPane tabbedPane;
	private JPanel pnSource, pnDest;
	private JTextPane sourceTextPane, destTextPane;
	private StyledDocument sourceDoc, destDoc;
	private Converter converter;

	public ConvertPane(){
		super();
		converter = new Converter();
		initComponent();
	}
	
	private void initComponent(){
		setBounds(5, 5, 900, 500);
		Container c = getContentPane();
		c.setLayout(new BorderLayout());
		
		initToolBar();
		
		addInternalFrameListener(this);
		
		setClosable(true);
		setResizable(true);
		
		tabbedPane = new JTabbedPane();
		initSourcePane();
		c.add(tabbedPane, BorderLayout.CENTER);
	}
	
	private void initSourcePane(){
		//pnSource = new JPanel();
		sourceTextPane = new JTextPane();
		JScrollPane scrollPane = new JScrollPane(sourceTextPane);
		scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		tabbedPane.addTab("Source", null, scrollPane, "Orginal Source code");
		
		//sourceTextPane.getStyledDocument().setDocumentFilter(new SourceCodeFilter());
		StyledDocument styledDoc = sourceTextPane.getStyledDocument();
		((AbstractDocument)styledDoc).setDocumentFilter(new SourceCodeFilter());
		styledDoc.addDocumentListener(new MyDocumentListener());
		sourcePopupMenu = new JPopupMenu();
		mniSourcePaste = new JMenuItem("Paste"); 
		mniSourcePaste.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent ae){
				String result = "";
				Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
				//odd: the Object param of getContents is not currently used
				Transferable contents = clipboard.getContents(null);
				boolean hasTransferableText =  (contents != null) &&
				  contents.isDataFlavorSupported(DataFlavor.stringFlavor);
				if (hasTransferableText) {
				  try {
					SimpleAttributeSet attributes = new SimpleAttributeSet();
					result = (String)contents.getTransferData(DataFlavor.stringFlavor);
					StyledDocument styledDoc = sourceTextPane.getStyledDocument();
					styledDoc.insertString(sourceTextPane.getCaretPosition(), result, attributes);
				  }
				  catch (UnsupportedFlavorException | IOException | BadLocationException ex){
					System.out.println(ex);
					ex.printStackTrace();
				  }
				}
			}
		});
		sourcePopupMenu.add(mniSourcePaste);
		sourceTextPane.addMouseListener(new PopupListener());
		
		//pnDest = new JPanel();
		destTextPane = new JTextPane();
		scrollPane = new JScrollPane(destTextPane);
		scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		//pnDest.add(scrollPane);
		tabbedPane.addTab("HTML Code", null, scrollPane, "HTML code");
		
		popupMenu = new JPopupMenu();
		mniCopySelection = new JMenuItem("Copy");
		mniCopySelection.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent ae){
				Clipboard clb = Toolkit.getDefaultToolkit().getSystemClipboard();
				StringSelection ss = new StringSelection(destTextPane.getSelectedText());
				clb.setContents(ss, ss);
			}
		});
		popupMenu.add(mniCopySelection);
		
		mniCopyAll = new JMenuItem("Copy");
		mniCopyAll.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent ae){
				Clipboard clb = Toolkit.getDefaultToolkit().getSystemClipboard();
				StringSelection ss = new StringSelection(destTextPane.getText());
				clb.setContents(ss, ss);
			}
		});
		popupMenu.add(mniCopyAll);
		
		destTextPane.addMouseListener(new PopupListener());
	}
	
	private void onChooseFile(){
		JFileChooser fileChooser = new JFileChooser();
		int retval = fileChooser.showOpenDialog(ConvertPane.this);
		if (retval == JFileChooser.APPROVE_OPTION) {
			File file = fileChooser.getSelectedFile();
			//log.append("Attaching file: " + file.getName()+ "." + newline);
			try{
				converter.readSourceFile(file);
				sourceDoc = sourceTextPane.getStyledDocument();
				ArrayList<String> linesOfCode = converter.getLinesOfCode();
				for(int i=0; i<linesOfCode.size(); i++)
					sourceDoc.insertString(sourceDoc.getLength(), linesOfCode.get(i) + "\n", null);
			}
			catch(IOException e1)
			{}
			catch(BadLocationException e2){}
			
		} else {
			//log.append("Attachment cancelled by user." + newline);
		}
	}
	
	private void onConvert(){
		int i;
		converter.doConvert();
		destDoc = destTextPane.getStyledDocument();
		List<String> gutter = converter.getGutter();
		List<String> linesOfCode = converter.getConvertedCodes();
		try{
			destDoc.insertString(destDoc.getLength(), "<div class=\"syntaxhighlighter\"><table><tr><td class=\"gutter\">\n", null);
			for(i=0; i<gutter.size(); i++){
				destDoc.insertString(destDoc.getLength(), gutter.get(i) + "\n", null);
			}
			destDoc.insertString(destDoc.getLength(), "</td>\n<td class=\"code\">\n", null);
			for(i=0; i<linesOfCode.size(); i++)
				destDoc.insertString(destDoc.getLength(), linesOfCode.get(i)+"\n", null);
				
			destDoc.insertString(destDoc.getLength(), "</td></tr></table></div>\n", null);
		}catch(BadLocationException ex1){
			ex1.printStackTrace();
		}
	}
	
	public void setParent(IMainAppFrame appFrame){
		parent = appFrame;
	}
	
	private void initToolBar(){
		toolBar = new JToolBar("Toolbar", JToolBar.HORIZONTAL);
		btnLoadSource = new JButton("Load source");
		btnLoadSource.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent ae){
				onChooseFile();
			}
		});
			
		btnConvert = new JButton("Convert");
		btnConvert.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent ae){
				onConvert();
			}
		});
		
		toolBar.add(btnLoadSource);
		toolBar.add(btnConvert);
		getContentPane().add(toolBar, BorderLayout.NORTH);
	}
	
	public void internalFrameClosing(InternalFrameEvent e) {
    }
 
    public void internalFrameClosed(InternalFrameEvent e) {
		
    }
 
    public void internalFrameOpened(InternalFrameEvent e) {
    }
 
    public void internalFrameIconified(InternalFrameEvent e) {
    }
 
    public void internalFrameDeiconified(InternalFrameEvent e) {
    }
 
    public void internalFrameActivated(InternalFrameEvent e) {
    }
 
    public void internalFrameDeactivated(InternalFrameEvent e) {
    }
	
	class PopupListener extends MouseAdapter{
		public void mousePressed(MouseEvent me){
			
		}
		
		public void mouseReleased(MouseEvent me){
			showPopup(me);
		}
		
		private void showPopup(MouseEvent me){
			if(me.isPopupTrigger()){
				if(me.getComponent() == sourceTextPane )
					sourcePopupMenu.show(me.getComponent(), me.getX(), me.getY());
				if(me.getComponent() == destTextPane )
					popupMenu.show(me.getComponent(), me.getX(), me.getY());
			}
		}
	}
	
	class MyDocumentListener implements DocumentListener {
        final String newline = "\n";

        public void insertUpdate(DocumentEvent e) {
			int offset = e.getOffset();
            int len = e.getLength();
            DocumentEvent.EventType eType = e.getType();
            //System.out.println("DOCUMENT CHANGED: " + offset + ", " + len + " type: " + eType);
        }
        public void removeUpdate(DocumentEvent e) {
			int offset = e.getOffset();
            int len = e.getLength();
            DocumentEvent.EventType eType = e.getType();
            //System.out.println("DOCUMENT CHANGED: " + offset + ", " + len + " type: " + eType);
        }
        public void changedUpdate(DocumentEvent e) {
            int offset = e.getOffset();
            int len = e.getLength();
            DocumentEvent.EventType eType = e.getType();
            
           //System.out.println("DOCUMENT CHANGED: " + offset + ", " + len + " type: " + eType);
        }
    }
    
    class SourceCodeFilter extends DocumentFilter {
		
		private MutableAttributeSet normalAtt;
		private MutableAttributeSet commentAtt;
		private MutableAttributeSet keywordAtt;
		private Pattern pattern;
		
		public SourceCodeFilter(){
			normalAtt = new javax.swing.text.SimpleAttributeSet();
			
			commentAtt = new javax.swing.text.SimpleAttributeSet();
			StyleConstants.setForeground(commentAtt, Color.GREEN);
			
			keywordAtt = new javax.swing.text.SimpleAttributeSet();
			StyleConstants.setBold(keywordAtt, true);
			StyleConstants.setForeground(keywordAtt, Color.BLUE);
			
			pattern = Pattern.compile(Constant.KEYWORD_PATTERN);
		}
		
		/**
		
			@param
				
		*/
		private int insertLine(StyledDocument doc, FilterBypass fb, int offs, String line) throws BadLocationException{
			List<WordRun> words = converter.processLine(line);
			String lastWord = "";
			Matcher matcher;
			int row=0, i = 0, len, lastSpace = getLastDelimiter(doc.getText(0, doc.getLength()));
			
			//test
			for(WordRun l: words)
				System.out.println("Word: " + l);
			
			//find the last word in the document
			System.out.println("[INFO] lastSpace: " + lastSpace);
			if(lastSpace>-1 && lastSpace < doc.getLength()-1){
				System.out.println("[INFO] 1 Offset: " + offs + "; Doc length: " + doc.getLength());
				lastWord = doc.getText(lastSpace+1, offs-lastSpace-1);
				
				lastWord = lastWord + words.get(0);
				System.out.println("[INFO] lastWord:" + lastWord);
				matcher = pattern.matcher(lastWord);
				len = doc.getLength()-(lastSpace>=0?lastSpace:0);
				if(matcher.find()){
					//System.out.println("[INFO] Match FOUND, last word:" + lastWord + "; offset: " + offs + "; Doc length: " + doc.getLength());
					super.replace(fb, lastSpace+1, len-1, lastWord, keywordAtt);
				}else{
					System.out.println("[INFO] Match NOT FOUND, last word:" + lastWord + "; offset: " + offs + "; lastSpace: " + lastSpace);
					super.replace(fb, lastSpace+1, len-1, lastWord, normalAtt);
				}
				offs += (lastSpace>=0?lastSpace:0) + len + 1;
				System.out.println("[INFO] 2 offset: " + offs + "; length: " + len + "; lastSpace: " + lastSpace);
				i = 1;
			}
			
			while(i < words.size()){
				lastWord = words.get(i).toString();
				System.out.println("[INFO] lastWord:" + lastWord);
				matcher = pattern.matcher(lastWord);
				if(matcher.find()){
					System.out.println("[INFO] Match FOUND, last word:" + lastWord + "; offset: " + offs + "; Doc length: " + doc.getLength());
					super.insertString(fb, offs, lastWord + " ", keywordAtt);
				}else{
					System.out.println("[INFO] Match NOT FOUND, last word:" + lastWord + "; offset: " + offs + "; Doc length: " + doc.getLength());
					super.insertString(fb, offs, lastWord + " ", normalAtt);
					System.out.println("[INFO] Doc length after insert: " + doc.getLength());
				}
				offs += lastWord.length() + 1;
				System.out.println("[INFO] 3 offset: " + offs + "; length: " + lastWord.length());
				i++;
			}
			super.insertString(fb, offs, "\n", normalAtt);
			System.out.println("[INFO] RETURN offset:" + offs);
			return offs+1;
		}
		
		public void insertString(FilterBypass fb, int offs, String str, AttributeSet a)
			throws BadLocationException {
			System.out.println("[INFO] offset: " + offs );
			StyledDocument doc = (StyledDocument) fb.getDocument();
			//int row=0;//, lastSpace = getLastDelimiter(doc.getText(0, doc.getLength()));
			String[] lines = str.split("\n");
			int offset = offs;
			for(String line: lines ){
				System.out.println("[insertString - INFO] BEFORE offset: " + offset );
				offset = insertLine(doc, fb, offset, line);
				System.out.println("[insertString - INFO] AFFTER offset: " + offset );
			}
		}

		public void insertString1(FilterBypass fb, int offs, String str, AttributeSet a)
			throws BadLocationException {
			System.out.println("[INFO] offset: " + offs );
			
			StyledDocument doc = (StyledDocument) fb.getDocument();
			int i, lastSpace = getLastDelimiter(doc.getText(0, doc.getLength()));
			String lastWord = "";
			
			//find the last word in the document
			if(lastSpace >= 0 && lastSpace < doc.getLength()-1){
				lastWord = doc.getText(lastSpace+1, offs-lastSpace-1);
			}
			
			//find the first word in the appending string and append it 
			//to the last word in document
			int l = getFirstDelimiter(str);
			if(l>=0)
				lastWord = lastWord + str.substring(0, l);
			
			//check if this word
			Matcher matcher = pattern.matcher(lastWord);
			if(matcher.find()){
				super.insertString(fb, lastSpace+1, lastWord, keywordAtt);
			}
			
			if(l>0)
				offs += l;
			
			i = l + 1;
			lastWord = "";
			while(i<str.length()){
				if(str.charAt(i)!=' ' && str.charAt(i)!='\t')
					lastWord = lastWord + str.charAt(i);
				else{
					lastWord = lastWord + str.charAt(i);
					matcher = pattern.matcher(lastWord);
					if(matcher.find()){
						System.out.println("[INFO] Match FOUND, last word:" + lastWord + "; offset: " + offs + "; Doc length: " + doc.getLength());
						super.insertString(fb, offs, lastWord, keywordAtt);
					}else{
						System.out.println("[INFO] Match NOT FOUND, last word:" + lastWord + "; offset: " + offs + "; Doc length: " + doc.getLength());
						super.insertString(fb, offs, lastWord, normalAtt);
					}
					offs += lastWord.length();
					lastWord = "";
				}
				i++;
			}
			
			matcher = pattern.matcher(lastWord);
			if(matcher.find()){
				super.insertString(fb, offs, lastWord, keywordAtt);
			}else
				super.insertString(fb, offs, lastWord, normalAtt);
			
			//System.out.println(str);
			//super.insertString(fb, offs, str, a);	
			
		}
		
		public void replace(FilterBypass fb, int offs, int length, String str, AttributeSet a) throws BadLocationException {
			StyledDocument doc = (StyledDocument) fb.getDocument();
			int lastSpace = getLastDelimiter(doc.getText(0, doc.getLength()));
			String lastWord = doc.getText(lastSpace+1, offs-lastSpace-1);
			
			if(str.length() == 1 && ( str.equals(" ") || str.equals("(") )){
				Matcher matcher = pattern.matcher(lastWord);
				if(matcher.find()){
					super.replace(fb, lastSpace+1, offs-lastSpace-1, lastWord, keywordAtt);
				}
				lastSpace = offs;
			}
			
			super.replace(fb, offs, length, str, normalAtt);
		}
		
		private int getLastDelimiter(String str){
			int i = str.length() - 1;
			
			while(i>=0){
				if ( str.charAt(i) ==' ' || str.charAt(i) =='\t' )
					return i;
				i--;
			}
			return -1;
		}
		
		private int getFirstDelimiter(String str){
			int i = 0;
			
			while(i < str.length()){
				if( str.charAt(i) ==' ' || str.charAt(i) =='\t' ) 
					return i;
				i++;
			}
			return -1;
		}

	}
}
